package com.cskaoyan.mall.search.service.impl;

import com.cskaoyan.mall.commons.exception.ExceptionProcessorUtils;
import com.cskaoyan.mall.dto.ProductDto;
import com.cskaoyan.mall.search.consts.SearchRetCode;
import com.cskaoyan.mall.search.converter.ProductConverter;
import com.cskaoyan.mall.search.dto.SearchRequest;
import com.cskaoyan.mall.search.dto.SearchResponse;
import com.cskaoyan.mall.search.entity.ItemDocument;
import com.cskaoyan.mall.search.entity.ItemDocumentStandardText;
import com.cskaoyan.mall.search.exception.SearchException;
import com.cskaoyan.mall.search.repository.Product2Repository;
import com.cskaoyan.mall.search.repository.ProductRepository;
import com.cskaoyan.mall.search.service.ProductSearchService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * @author LY
 * @since 2022/06/25 22:33
 */
@Slf4j
@Service
public class ProductSearchServiceImpl implements ProductSearchService {

    @Autowired
    ProductRepository productRepository;

    @Autowired
    Product2Repository product2Repository;

    @Autowired
    ProductConverter productConverter;

    @Autowired
    ElasticsearchRestTemplate restTemplate;

    @Override
    public SearchResponse search(SearchRequest request) {

        SearchResponse<ProductDto> response = new SearchResponse<>();

        try {
            request.requestCheck();

            String keyword = request.getKeyword();

            NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

            // 复合查询关键字
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

            QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(keyword).field("title").field("sellPoint");
            boolQueryBuilder.must(queryStringQueryBuilder);

            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price").gte(request.getPriceGt()).lte(request.getPriceLte());
            boolQueryBuilder.filter(rangeQueryBuilder);

            queryBuilder.withQuery(boolQueryBuilder);

            // 高亮查询

            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("title").preTags("<font color='red'>").postTags("</font>");
            queryBuilder.withHighlightBuilder(highlightBuilder);

            // 设置分页参数
            PageRequest pageRequest = null;
            String sort = request.getSort();
            if ("".equals(sort)) {
                log.info("综合排序");
                pageRequest = PageRequest.of(request.getCurrentPage(), request.getPageSize());
            } else if ("1".equals(sort)) {
                log.info("价格从低到高");
                pageRequest = PageRequest.of(request.getCurrentPage(), request.getPageSize(), Sort.by(Sort.Order.asc(
                        "price")));
            } else if ("-1".equals(sort)) {
                log.info("价格从高到低");
                pageRequest = PageRequest.of(request.getCurrentPage(), request.getPageSize(), Sort.by(Sort.Order.desc(
                        "price")));
            } else {
                log.error("ProductSearchServiceImpl.search Occur Exception : 排序参数非法");
                throw new SearchException(SearchRetCode.STRING_EMPTY.getCode(), SearchRetCode.STRING_EMPTY.getMsg());
            }
            queryBuilder.withPageable(pageRequest);

            SearchHits<ItemDocument> searchHits = restTemplate.search(queryBuilder.build(), ItemDocument.class);
            List<ProductDto> productDtos = new ArrayList<>();
            long total = searchHits.getTotalHits();
            if (total == 0) {
                SearchHits<ItemDocumentStandardText> searchStandardTextHits = restTemplate.search(queryBuilder.build(), ItemDocumentStandardText.class);

                total = searchStandardTextHits.getTotalHits();
                List<SearchHit<ItemDocumentStandardText>> hits = searchStandardTextHits.getSearchHits();
                hits.forEach(hit -> {
                    ItemDocumentStandardText itemDocumentStandard = hit.getContent();
                    ProductDto productDto = productConverter.itemStandards2Dto(itemDocumentStandard);
                    List<String> title = hit.getHighlightField("title");
                    productDto.setProductName(title.get(0));
                    productDtos.add(productDto);
                });

            } else {

                List<SearchHit<ItemDocument>> hits = searchHits.getSearchHits();
                hits.forEach(hit -> {
                    ItemDocument itemDocument = hit.getContent();
                    ProductDto productDto = productConverter.item2Dto(itemDocument);
                    List<String> title = hit.getHighlightField("title");
                    productDto.setProductName(title.get(0));
                    productDtos.add(productDto);
                });
            }
            response.setData(productDtos);
            response.setTotal(total);
            response.setCode(SearchRetCode.SUCCESS.getCode());
            response.setMsg(SearchRetCode.SUCCESS.getMsg());
        } catch (Exception e) {
            log.error("ProductSearchServiceImpl.search Occur Exception :" + e);
            e.printStackTrace();
            ExceptionProcessorUtils.wrapperHandlerException(response, e);
        }
        return response;
    }

    @Override
    public SearchResponse fuzzySearch(SearchRequest request) {
        SearchResponse<ProductDto> response = new SearchResponse<>();

        try {
            request.requestCheck();

            List<ItemDocument> itemDocuments = productRepository.matchSearch(request.getKeyword());

            if (CollectionUtils.isEmpty(itemDocuments)) {
                List<ItemDocumentStandardText> itemDocumentStandardTexts = product2Repository.matchSearch(request.getKeyword());
                itemDocuments = productConverter.itemDocumentStandards2Document(itemDocumentStandardTexts);
            }
            List<ProductDto> productDtos = productConverter.items2Dto(itemDocuments);

            response.setData(productDtos);
            response.setCode(SearchRetCode.SUCCESS.getCode());
            response.setMsg(SearchRetCode.SUCCESS.getMsg());

        } catch (Exception e) {
            log.error("ProductSearchServiceImpl.fuzzySearch Occur Exception :" + e);
            e.printStackTrace();
            ExceptionProcessorUtils.wrapperHandlerException(response, e);
        }

        return response;
    }
}
